﻿#include <dp/thread/thread.h>

DP_BEGIN

Thread::Thread(SIZE_T stackSize)
	: WaitHandle()
	, m_nThreadID(0)
	, m_func(NULL)
	, m_nStackSize(stackSize)
	, m_eStatus(StatusInitialized)
{
}

void Thread::Join()
{
	if(StatusStarted != m_eStatus)
		throw ThreadException("InvalidOperation: Must call operator() before call Join.");

	TryJoin(INFINITE);
}

BOOL Thread::TryJoin(DWORD timeout)
{
	if(StatusStarted != m_eStatus)
		throw ThreadException("InvalidOperation: Must call operator() before call TryJoin.");
	
	// rewrite
	try{
		return Wait(timeout);
	}
	catch (Win32Exception& e){
		throw ThreadException(__FUNCTION__, e.GetErrorCode());
	}
}

void Thread::Terminate(DWORD dwExitCode)
{
	if(StatusStarted != m_eStatus)
		throw ThreadException("InvalidOperation: Must call operator() before call Terminate.");

	BOOL ret = ::TerminateThread(Handle, dwExitCode);
	if(!ret) {
		std::stringstream ss;
		ss << "TerminateThread(" << Handle << ")";
		throw ThreadException(ss.str());
	}
}

DWORD Thread::Resume() 
{
	if(StatusStarted != m_eStatus)
		throw ThreadException("InvalidOperation: Must call operator() before call Resume.");
	
	DWORD ret = ::ResumeThread(Handle);
	if((DWORD)-1 == ret) {
		std::stringstream ss;
		ss << "ResumeThread(" << Handle << ")";
		throw ThreadException(ss.str());
	}
	
	return ret;							// the thread's suspend count
}

DWORD Thread::Suspend()
{
	if(StatusStarted != m_eStatus)
		throw ThreadException("InvalidOperation: Must call operator() before call Suppend.");

	DWORD ret = ::SuspendThread(Handle);
	if((DWORD)-1 == ret) {
		std::stringstream ss;
		ss << "SuspendThread(" << Handle << ")";
		throw ThreadException(ss.str());
	}

	return ret;							// the thread's suspend count
}

void Thread::SetPriority(ThreadPriority priority)
{
	if(StatusStarted != m_eStatus)
		throw ThreadException("InvalidOperation: Must call operator() before call SetPriority.");

	if(!::SetThreadPriority(Handle, priority)) {
		std::stringstream ss;
		ss << "SetThreadPriority(" << Handle << ", " << priority << ")";
		throw ThreadException(ss.str());
	}
}

Thread::ThreadPriority Thread::GetPriority()
{
	if(StatusStarted != m_eStatus)
		throw ThreadException("InvalidOperation: Must call operator() before call GetPriority.");

	ThreadPriority ret = (ThreadPriority)::GetThreadPriority(Handle);

	if(THREAD_PRIORITY_ERROR_RETURN == ret)	{
		std::stringstream ss;
		ss << "GetThreadPriority(" << Handle << ")";
		throw ThreadException(ss.str());
	}

	return ret;
}

Thread::ThreadStatus Thread::GetStatus()
{
	return m_eStatus;
}

// return 0 if thread has been terminated
DWORD Thread::GetID()
{
	if(StatusStarted != m_eStatus)
		throw ThreadException("InvalidOperation: Must call operator() before call GetID.");
	
	// added to return 0 if thread has been terminated (> win2003)
	// remove because GetThreadId require > win2003
	//DWORD ret;
	//ret = GetThreadId(Handle);
	//if(0 == ret)
	//	throw Win32Exception(__FUNCTION__);

	return m_nThreadID;
}

// static
void Thread::Sleep(DWORD dwMiliseconds)
{
	::Sleep(dwMiliseconds);
}

// static 
void Thread::Exit(DWORD dwExitCode)
{
	// ExitThread is the preferred method of exiting a thread in C code.
	// However, in C++ code, the thread is exited before any destructors can be called
	// or any other automatic cleanup can be performed.
	// Therefore, in C++ code, you should return from your thread function.
	//::ExitThread(dwExitCode);

	_endthreadex(dwExitCode);
}

// You should Join the thread before the delete thread object
Thread::~Thread()
{
	if (!Handle) 
		return;

	// B1: Waits thread function to terminate. If it cannot terminate use Terminate method
	// to kill it.
	//
	// Cứ 2 giây trả hỏi một lần cho đến khi người dùng chấp nhận terminate hoặc thread
	// function tự kết thúc.
	//
	// TODO: add config to change to default timeout
	while(!TryJoin(2000)) {
		// TODO: Add button "No to all"
		std::stringstream ss;
		ss << "Thread[" << m_name << "]";

		int ans =
		MessageBoxA(
			0
			, "A thread did not response.\n" "Continue waiting?\n"
			, ss.str().c_str()
			, MB_YESNO | MB_HELP);

		if(IDNO == ans)
			Terminate(0xFFFFFFFF);
	}

	// B2: Now the thread function has been terminated for sure, let
	delete m_func;						// delete bind object
}

BOOL Thread::Create(BOOL start)
{
	DWORD dwCreationFlags = start ? 0 : CREATE_SUSPENDED;
	
	Handle = (HANDLE)_beginthreadex(
		0,
		(unsigned int)m_nStackSize,
		ThreadProc,
		this,
		dwCreationFlags,
		&m_nThreadID);

	//Handle = ::CreateThread ( 
	//	NULL,							// default security attributes
	//	m_nStackSize,
	//	ThreadProc,						// thread function 
	//	this,							// argument to thread function 
	//	dwCreationFlags,
	//	&m_nThreadID);					// returns the thread identifier 

	// return m_handle make a warning so I changed it.
	return (NULL != Handle);
}

void Thread::InternalStart()
{
	if(StatusInitialized != m_eStatus)
		throw ThreadException(
				"InvalidOperation:"
				"Call InternalStart method only once after initialization for each thread object.");

	BOOL ret = Create(TRUE);

	if(!ret) {
		std::stringstream ss;
		ss << "Thread.Start()";
		throw ThreadException(ss.str());
	}

	m_eStatus = StatusStarted;
}

unsigned  __stdcall Thread::ThreadProc(void *lpParam)
{
	Thread*	thread	= (Thread*)lpParam;
	Bind*	m_func	= thread->m_func;
	
	(*m_func)();						// gọi virtual Bind::operator()

	return 0;
} 

DP_END
